import { Subject, Subscription, BehaviorSubject } from "rxjs";
import { RxOp } from "./ReactiveOp";
import { pairwise } from "rxjs";

import { _decorator } from 'cc';
const { ccclass, property } = _decorator;

@ccclass
export class ReactiveMap<T extends keyof any, U> {

    static Create<T extends keyof any, U>(): ReactiveMap<T, U> {
        return new ReactiveMap<T, U>().init();
    }

    map: Record<T, U>;

    _subject: Subject<{ op: RxOp, k?: T, v?: U }> = null;

    _count_subject: BehaviorSubject<number> = null;
    _exist_subject: Subject<{ op: RxOp }> = null; // 用于map的变成’开始存在‘和变成’空‘的可观察对象

    init(): ReactiveMap<T, U> {
        this.map = <Record<T, U>>{};
        this._count_subject = new BehaviorSubject<number>(0);
        this._exist_subject = new Subject<{ op: RxOp }>();
        this._subject = new Subject<{ op: RxOp, k?: T, v?: U }>();
        this._count_subject.pipe(pairwise()).subscribe(pair => {
            if (pair[0] == 0 && pair[1] > 0) this._exist_subject.next({ op: RxOp.haveItem });
            if (pair[0] > 0 && pair[1] == 0) this._exist_subject.next({ op: RxOp.noItem });
        });
        return this;
    }

    get(key: any) {
        return this.map[key];
    }

    set(key: any, value: U) {
        if (this.map[key] == null) {
            this.map[key] = value;
            this._subject.next({ op: RxOp.add, k: key, v: value });
            this._count_subject.next(this._count_subject.value + 1);
        } else {
            this.map[key] = value;
            if (value == null) {
                this._subject.next({ op: RxOp.delete, k: key, v: this.map[key] });
            } else {
                this._subject.next({ op: RxOp.change, k: key, v: value });
            }
        }
    }

    remove(key: any) {
        if (this.map[key] != null) {
            let value = this.map[key];
            delete this.map[key];
            this._subject.next({ op: RxOp.delete, k: key, v: value });
            this._count_subject.next(this._count_subject.value - 1);
        }
    }

    clear() {
        for (let key in this.map) {
            if (this.map[key] != null) {
                this._subject.next({ op: RxOp.delete, k: key, v: this.map[key] });
            }
        }
        this.map = <Record<T, U>>{};
        this._count_subject.next(0);
    }

    count(): number {
        return this._count_subject.value;
    }

    countRecount(): number {
        let count = 0;
        for (let key in this.map) {
            if (this.map[key] != null) count++;
        }
        return count;
    }

    subscribe(callback: (value: { op: RxOp, k?: T, v?: U }) => void): Subscription {
        return this._subject.subscribe(callback);
    }
}
